#include "lib/print.h"
#include "dev/uart.h"
#include "dev/console.h"
#include "lock/lock.h"
#include <stdarg.h>

const char digit[] = "0123456789ABCDEF";

// 保证一次printf可以完整输出, 不会在中途发生进程切换
static spinlock_t print_spinlock;

volatile int panicked = 0;

static void print_10(uint64 xx, bool islong, bool isunsigned) 
{    
    char out[30];
    int index = 29;

    out[index--] = 0; // 字符串终止符

    if(xx == 0) {
        out[index--] = '0';
        goto END;
    }

    if(islong && isunsigned) {
        while(xx != 0) {
            out[index--] = xx % 10 + '0';
            xx = xx / 10;
        }      
    } else if(islong) {
        int64 tmp = (int64)xx;
        bool neg = false;
        if(tmp < 0) {
            neg = true;
            tmp = -tmp;
        }
        while (tmp != 0) {
            out[index--] = tmp % 10 + '0';
            tmp = tmp / 10;
        }
        if(neg) out[index--] = '-';
    } else if(isunsigned) {
        uint32 tmp = (uint32)xx;
        while (tmp != 0) {
            out[index--] = tmp % 10 + '0';
            tmp = tmp / 10;
        }
    } else {
        int32 tmp = (int32)xx;
        bool neg = false;
        if(tmp < 0) {
            neg = true;
            tmp = -tmp;
        }
        while (tmp != 0) {
            out[index--] = tmp % 10 + '0';
            tmp = tmp / 10;
        }
        if(neg) out[index--] = '-';
    }
END:
    cons_puts((out+index+1));
}

static void print_16(uint64 xx,bool islong) 
{    
    int index = 29;
    char out[30];
    out[index--] = 0;

    if(islong) {
        for(int i=0; i<16; i++) {
            out[index--] = digit[xx % 16];
            xx = xx >> 4;
            if( (i+1) % 4 == 0 )
                out[index--] = '-';
        }
    } else {
        uint32 tmp = (uint32)xx;
        for(int i=0; i<8; i++) {
            out[index--] = digit[tmp % 16];
            tmp = tmp >> 4;
            if( (i+1) % 4 == 0 ) 
                out[index--] = '-'; 
        }   
    }
    out[index+1] = 'x';
    out[index--] = '0';
    cons_puts((out+index+1));
}

static void print_str(char* s) 
{
    if(s == NULL) 
        cons_puts("NULL\0");
    else 
        cons_puts(s);
}

/* 外部函数 */

void print_init() 
{
    spinlock_init(&print_spinlock,"print");
}

/*
    %d        int
    %ld       long long
    %ud       unsinged int
    %uld      unsigned long long
    %x        十六进制32bit
    %p        十六进制64bit
    %c        char
    %s        char*        
*/
int printf(const char* fmt, ...) 
{
    if(fmt == NULL) { 
        panic("try to printf null\n");
        return -1;
    }

    va_list vl;                 // 读取参数列表
    bool flag = false;          // 上一个字符是%吗
    bool unsigned_flag = false;
    bool long_flag = false;
    char c;

    va_start(vl,fmt);
    spinlock_acquire(&print_spinlock);

    for(int i = 0; fmt[i] != 0; i++) {
        c = fmt[i];
        if(!flag) {
            if(c == '%') {
                flag = true;
                unsigned_flag = false;
                long_flag = false;
            } else {
                uart_putc(c);
            }
            continue;
        }
        switch (c) {
            case 'u':
                unsigned_flag = true;
                break;
            case 'l':
                long_flag = true;
                break;
            case 'd':
                if(long_flag) {
                    print_10(va_arg(vl,uint64),long_flag,unsigned_flag);
                } else {
                    print_10((uint64)(va_arg(vl,uint32)),long_flag,unsigned_flag);
                }
                break;
            case 'x':     
                print_16((uint64)(va_arg(vl,uint32)),false);
                break;
            case 'p':
                print_16(va_arg(vl,uint64),true);
                break;
            case 's':
                print_str(va_arg(vl,char*));
                break;
            case 'c':
                uart_putc((char)(va_arg(vl,int)));
                break;
            default:
                uart_putc(c);
                flag = false;
                break;
        }
    }
    
    spinlock_release(&print_spinlock);
    va_end(vl);
    return 0;
}

void panic(const char* warning)
{
    printf("panic! %s\n",warning);
    panicked = 1;
    while(1);
}

void assert(bool condition, const char* waring)
{
    if(!condition) {
        if(waring != NULL) {
            panic(waring);
        } else {
            panic("failed assert");
        }
    }
}